#!/usr/bin/env bash

# Exit immediately when stderr is written to
set -eu -o pipefail

LOG_FILE=_error.txt
fatal() {
	for arg in "$@"; do
	    echo -e " *  $arg" >&2
	done

	exit 1
}

UPDATE_URL=https://sdk-os-static.mihoyo.com/hk4e_global/mdk/launcher/api/resource?launcher_id=10
#UPDATE_URL=http://127.0.0.1/resource.json
CONFIG=config.ini
ANCHOR=UnityPlayer.dll

SCRIPT_PATH=`cd $(dirname $0); echo $PWD`
# Dedicated download directory for aria2c: it stores the index file in the same directory as target file
DOWNLOAD_DIR=$PWD/../_update_gi_download
DOWNLOAD_APPS=("aria2c|aria2c -c" "wget|wget -c" "curl|curl -C")

# Perform path sanity checks
# There is a good reason for this check. Do not pollute the game directory.
test \! -e "$SCRIPT_PATH/$ANCHOR" || fatal \
	"Please move this script outside the game directory prior executing." \
	" -> See README.md for proper installation instructions"

test -e "$CONFIG" || fatal \
	"Game information file $CONFIG not found." \
	"Make sure 'Genshin Impact Game' is the current working directory."



################ Find a supported download executable
dl_app=""
for appname in ${DOWNLOAD_APPS[@]}; do
	dl_app_try=${appname%%|*}
	if command -v $dl_app_try &>/dev/null; then
		dl_app=${appname#*|}
		break
	fi
done

test -n "$dl_app" || fatal \
	"No downloader application found." \
	"Please install 'aria2c' (recommended), 'wget' or 'curl'."

installed_version=`sed -n '/game_version=/{s/^.*=//;s/\r//p}' $CONFIG`

test -n "$installed_version" || fatal "Cannot read game_version from $CONFIG"
echo "Installed version:  $installed_version"



################ Download the version information JSON file

# wget and curl can download to stdout, but aria2c can't.
# so we just create temp dir for it and read any downloaded file whose name can differ too
tmp_path=$(mktemp -d)
trap "rm -rf '$tmp_path'" EXIT

update_content=$(cd "$tmp_path" && "$dl_app" "$UPDATE_URL" >"$LOG_FILE" 2>&1 && cat resource*) || fatal \
	"Failed to download version info. Check your internet connection." \
	"$LOG_FILE may contain some useful information."

update_content=$(sed "s/{/&\n/g;s/}/\n&/g" <<< "$update_content")

# find latest version: extract from JSON format
latest_version_content=$(sed -n '/"latest":/,/^}/{/"version":/!d;s/,/\n/g;s/"//g;p}' <<< "$update_content")

declare -A version_info
while read -r keyvalue; do
	version_info[${keyvalue%%:*}]=${keyvalue#*:}
done <<< "$latest_version_content"

echo "Latest version:     ${version_info[version]}"

if [ "${version_info[version]}" = "$installed_version" ]; then
	echo "==> Client is up to date."
	exit 0
fi

# find suitable patch: extract version information
diffs_content=$(sed -n '/"diffs":/,/^}]/{/"name":/!d;s/"//g;p}' <<< "$update_content")

patch_strategy="full" # download full client
while read -r diff; do
	diff_content=$(sed 's/,/\n/g' <<< "$diff")

	unset diff_info
	declare -A diff_info
	while read -r keyvalue; do
		diff_info[${keyvalue%%:*}]=${keyvalue#*:}
	done <<< "$diff_content"

	if [ "${diff_info[version]}" == "$installed_version" ]; then
		patch_strategy="diff"
		break
	fi
done <<< "$diffs_content"

if [ "$patch_strategy" == "diff" ]; then
	download_url=${diff_info[path]}
	patch_size=${diff_info[size]}
	patch_md5=${diff_info[md5]}
	patch_type="incremental update"
else
	download_url=${version_info[path]}
	patch_size=${version_info[size]}
	patch_md5=${version_info[md5]}
	patch_type="full client"
	echo "[WARNING] Cannot find any suitable upgrade to ${version_info[version]}. Will download full client!"
fi

# Unknown origin of the factor 10 (blame the devs)
patch_size_mib=$((${version_info[size]} / 10 / 1024 / 1024))
echo "URL: $download_url"
echo "Download size:      ${patch_size_mib} MiB (${patch_type})"
echo

# confirm update
while :; do
	read -r -p "Start/continue update? [Y/n]: " input
	case "$input" in
		Y|y|'')
			break
			;;

		n|N)
			exit 0
			;;
	esac
done



######## Start/continue downloading patch or full client

archive_file="$DOWNLOAD_DIR/${download_url##*/}"
archive_file_completed="${archive_file}.completed"

if [ -f "$archive_file_completed" ]; then
	# patch was already downloaded but something went wrong when applying it
	echo "-- Found existing archive: ${download_url##*/} is already downloaded"
else
	mkdir -p "$DOWNLOAD_DIR"
	cd "$DOWNLOAD_DIR"
	$dl_app $download_url
	cd - >/dev/null # Return to previous dir
	touch "$archive_file_completed"
fi

# validate size and md5
# idk how to interpret size
#actual_patch_size=$(stat -c%s "$archive_file")
#test $patch_size -eq $actual_patch_size || fatal \
#	"Downloaded patch size doesn't match expected: $patch_size != $actual_patch_size"

echo "-- Verifying MD5 checksum..."
md5sum -c <<< "$patch_md5 $archive_file"



######## Apply clean client update

patcher_dir="${SCRIPT_PATH}/../${version_info[version]//./}"

# Run 'patch_revert.sh' of the newest directory
if [ -e launcher.bat ]; then
	test -d "$patcher_dir" || fatal "Can't revert wine compat patch. '$patcher_dir' doesn't exist"

	echo
	echo "============== Reverting previous Wine patch ==============="
	bash "$patcher_dir/patch_revert.sh"
	echo "============================================================"
	echo
fi


# Unpack the game files and remove old ones according to deletefiles.txt

echo "-- Updating game files...."

# Prevent deleting of the possibly necessary files in case of full update
rm -f deletefiles.txt

unzip -o "$archive_file" >>"$LOG_FILE"

if [ -f deletefiles.txt ]; then
	while read -r delme; do
		rm -f "$delme"
	done < deletefiles.txt
	rm deletefiles.txt
fi

sed -i "s/game_version=$installed_version/game_version=${version_info[version]}/" "$CONFIG"

# If we got this far, this was a successful update. Clean up old files.
rm -r "$DOWNLOAD_DIR"
rm -f "$LOG_FILE"

echo "==> Update to version ${version_info[version]} completed"



######## Run wine compatibility patch script

echo
echo "================= Applying new Wine patch =================="
bash "$patcher_dir/patch.sh"
if [ -f "$patcher_dir/patch_anti_logincrash.sh" ]; then
	bash "$patcher_dir/patch_anti_logincrash.sh"
fi
echo "============================================================"
echo "==> Update script completed successfully"
exit 0
